module ALU (
    input                       clock,
    input                       reset,
    input                       flush,
    input                       valid,
    input	logic [11:0]		operation,
    input	logic [63:0]		src1,
    input	logic [63:0]		src2,
    input	logic 				word,
    input   logic 				unsign,
    input   logic [1:0] 	    mul_signs,
    input   logic               mul_high,
    output  logic [63:0]		result,
    output                      done
);

/* ------ 64-bit adder ------ */
    logic [63:0]	adder_a;
    logic [63:0]	adder_b;
    logic			adder_cin;
    logic [63:0]	adder_sum;
    logic			adder_cout;
/* ------ 64-bit adder ------ */

/* ---------------------- operation flag ---------------------- */
    wire    add_op	= operation[00];	// 加法操作
    wire    sub_op	= operation[01];	// 减法操作
    wire    slt_op	= operation[02];	// 小于置位操作
    wire    and_op	= operation[03];	// 按位与操作
    wire    or_op	= operation[04];	// 按位或操作
    wire    xor_op	= operation[05];	// 按位异或
    wire    sll_op	= operation[06];	// 逻辑左移
    wire    srl_op	= operation[07];	// 逻辑右移
    wire    sra_op	= operation[08];	// 算术右移
    wire    mul_op	= operation[09];	// 乘法
    wire    div_op	= operation[10];	// 除法
    wire    rem_op	= operation[11];	// 取余

/* ---------------------- operation flag ---------------------- */


    /* =============== ADD & SUB operation =============== */
    assign	adder_a   = src1;
    assign	adder_b   = (sub_op | slt_op) ? ~src2 : src2;
    assign	adder_cin = (sub_op | slt_op) ? 1'b1 : 1'b0;
    /* verilator lint_off WIDTH */
    assign	{ adder_cout, adder_sum } = adder_a + adder_b + adder_cin;

    wire [63:0] add_sub_result		= adder_sum;
    wire [31:0] addw_subw_result	= adder_sum[31:0];
    /* =============== ADD & SUB operation =============== */


    /* ================== SLT operation ================== */
    wire    slt_s	= ( src1[63]  & ~src2[63])
                    | ((src1[63] ~^  src2[63]) & adder_sum[63]);
    wire    slt_u	= ~adder_cout;

    wire [63:0] slt_result = { 63'h0, unsign ? slt_u : slt_s };
    wire [31:0] sltw_result = slt_result[31:0];
    /* ================== SLT operation ================== */


    /* ================ Bitwise operation ================ */
    wire [63:0] and_result	= src1 & src2;
    wire [31:0] andw_result	= and_result[31:0];

    wire [63:0] or_result	= src1 | src2;
    wire [31:0] orw_result	= or_result[31:0];
    
    wire [63:0] xor_result	= src1 ^ src2;
    wire [31:0] xorw_result	= xor_result[31:0];
    /* ================ Bitwise operation ================ */


    /* ========================== Shift operation ========================== */
    wire [5:0] shamt = { word ? 1'b0 : src2[5], src2[4:0] };

    // Left-shift operation
    wire [63:0] sll_result = src1 << shamt;
    wire [31:0] sllw_result = sll_result[31:0];

    // Right-shift operation
    wire [127:0] sr_operand = word ? { {96{sra_op & src1[31]}}, src1[31:0] } : { {64{sra_op & src1[63]}}, src1[63:0] };
    /* verilator lint_off UNUSEDSIGNAL */
    wire [127:0] sr128_result = sr_operand >> shamt;
    wire [63:0] sr_result = sr128_result[63:0];
    wire [31:0] srw_result = sr128_result[31:0];
    /* ========================== Shift operation ========================== */


    /* ========================== MUL operation ========================== */

    // WallaceMultiplier #(
    //     .WIDTH(64)
    // ) multiplier (
    //     .clock  (clock),
    //     .reset  (reset),
    //     .x      (src1),
    //     .y      (src2),
    //     .signs  ({1'b1, 1'b1}),
    //     .result (mul_result)
    // );
    // logic mul_done;
    // always_ff @(posedge clock) begin
    //     if (reset) begin
    //         mul_done <= 1'b0;
    //     end
    //     else if (mul_op & ~mul_done) begin
    //         mul_done <= 1'b1;
    //     end
    //     else begin
    //         mul_done <= 1'b0;
    //     end
    // end
    // // assign mul_result = src1 * src2;
    // assign mulw_result = mul_result[31:0];

    logic [63:0] mul_result;
    logic [31:0] mulw_result;

    logic mul_valid;
    logic mul_ready;
    logic mul_done;
    logic [127:0] mul128_result;
    ShiftMultiplier #(
        .WIDTH(64)
    ) multiplier (
        .clock  (clock),
        .reset  (reset),
        .x      (src1),
        .y      (src2),
        .signs  (mul_signs),
        .valid  (mul_valid),
        .ready  (mul_ready),
        .result (mul128_result),
        .done   (mul_done)
    );
    assign mul_valid = valid & mul_op & ~mul_doing & ~mul_done;
    logic mul_doing;
    always_ff @(posedge clock) begin
        if (reset) begin
            mul_doing <= 1'b0;
        end
        else if (mul_done) begin
            mul_doing <= 1'b0;
        end
        else if (mul_valid & mul_ready) begin
            mul_doing <= 1'b1;
        end
    end
    assign mul_result = mul_high ? mul128_result[127:64] : mul128_result[63:0];
    assign mulw_result = mul128_result[31:0];

    /* ========================== MUL operation ========================== */

    /* ========================== DIV&REM 64 ========================== */
    logic        dr64_op;
    logic [63:0] dr64_q;
    logic [63:0] dr64_r;
    logic        dr64_valid;
    logic        dr64_ready;
    logic        dr64_done;
    assign dr64_op = (div_op | rem_op) & ~word;
    Divider #(.WIDTH(64)) divider64 (
        .clock      (clock),
        .reset      (reset),
        .x          (src1),
        .y          (src2),
        .div_signed (~unsign),
        .flush      (flush),
        .valid      (dr64_valid),
        .ready      (dr64_ready),
        .q          (dr64_q),
        .r          (dr64_r),
        .done       (dr64_done)
    );
    logic        dr64_doing;
    always_ff @(posedge clock) begin
        if (reset) begin
            dr64_doing <= 1'b0;
        end
        else if (dr64_done) begin
            dr64_doing <= 1'b0;
        end
        else if (dr64_valid & dr64_ready) begin
            dr64_doing <= 1'b1;
        end
    end
    assign dr64_valid = valid & dr64_op & ~dr64_doing & ~dr64_done;

    wire [63:0] div_result  = dr64_q;
    wire [63:0] rem_result  = dr64_r;
    /* ========================== DIV&REM 64 ========================== */


    /* ========================== DIV&REM 32 ========================== */
    logic        dr32_op;
    logic [31:0] dr32_q;
    logic [31:0] dr32_r; 
    logic        dr32_valid;
    logic        dr32_ready;
    logic        dr32_done;
    assign dr32_op = (div_op | rem_op) & word;
    Divider #(.WIDTH(32)) divider32 (
        .clock      (clock),
        .reset      (reset),
        .x          (src1),
        .y          (src2),
        .div_signed (~unsign),
        .flush      (flush),
        .valid      (dr32_valid),
        .ready      (dr32_ready),
        .q          (dr32_q),
        .r          (dr32_r),
        .done       (dr32_done)
    );
    logic        dr32_doing;
    always_ff @(posedge clock) begin
        if (reset) begin
            dr32_doing <= 1'b0;
        end
        else if (dr32_done) begin
            dr32_doing <= 1'b0;
        end
        else if (dr32_valid & dr32_ready) begin
            dr32_doing <= 1'b1;
        end
    end
    assign dr32_valid = valid & dr32_op & ~dr32_doing & ~dr32_done;

    wire [31:0] divw_result  = dr32_q;
    wire [31:0] remw_result  = dr32_r;
    /* ========================== DIV&REM 32 ========================== */

/* ---------------------------------------- result ---------------------------------------- */
    wire [31:0] result_32 = ({32{add_op | sub_op}}	& addw_subw_result)	|
                            ({32{slt_op         }}	& sltw_result     )	|
                            ({32{and_op         }}	& andw_result     )	|
                            ({32{or_op          }}	& orw_result      )	|
                            ({32{xor_op         }}	& xorw_result     )	|
                            ({32{sll_op         }}	& sllw_result     )	|
                            ({32{sra_op | srl_op}}	& srw_result      ) |
                            ({32{mul_op         }}	& mulw_result     )	|
                            ({32{div_op         }}	& divw_result     )	|
                            ({32{rem_op         }}	& remw_result     )	;

    wire [63:0] result_64 = ({64{add_op | sub_op}} & add_sub_result) |
                            ({64{slt_op         }} & slt_result    ) |
                            ({64{and_op         }} & and_result    ) |
                            ({64{or_op          }} & or_result     ) |
                            ({64{xor_op         }} & xor_result    ) |
                            ({64{sll_op         }} & sll_result    ) |
                            ({64{sra_op | srl_op}} & sr_result     ) |
                            ({64{mul_op         }} & mul_result    ) |
                            ({64{div_op         }} & div_result    ) |
                            ({64{rem_op         }} & rem_result    ) ;
    assign result = word ? {{32{result_32[31]}}, result_32} : result_64;
/* ---------------------------------------- result ---------------------------------------- */

    assign done = (mul_op & mul_done) 
                | (dr64_op & dr64_done) 
                | (dr32_op & dr32_done)
                | ~(mul_op | dr64_op | dr32_op);

endmodule
